<?php
/**
 * Core application logic
 *
 * Contains methods that can be reused all over the 
 * application, all the methods in this class must be
 * static as only one application instance will run at 
 * time
 *
 * @category   Core
 * @package    Core_App
 * @copyright  
 * @version    $Id: App.php 12 2011-04-09 08:58:51Z sandeep.manne@gmail.com $
 */

require_once "Core/Model.php";
require_once "Zend/Config/Ini.php";

class App extends Core_Model
{
    private static $_version = "0.1 beta";
    
    public function __construct()
    {
        throw new Exception("Only one instance of application can run at a time");
    }

    /**
     * Return Module
     * @param String $moduleName
     * @throws Exception
     * @return Core_Module_Interface
     */
    public static function getModule($moduleName)
    {
    	try {
            require_once $moduleName."/etc/Module.php";
            $module = new Module();
            if ($module instanceof Core_Module_Interface) {
            	return $module;
            } else {
            	throw new Exception("Illegal Module");
            }
        } catch (Exception $e){
            throw new Exception($e->getMessage());
        }
    }
    
    /**
     * Returns application config
     * 
     * @param boolean $refresh     - returns o/p from cache if set to false
     * @param string  $returnArray - returns config in array format if set to true 
     * @return Zend_Config_Ini|array
     */
    public static function getConfig($refresh = false, $returnArray = false)
    {
        $cacheId = 'appconfig';
        if ($returnArray) {
            $cacheId = "appconfigArray";
        }
        $cache = App::getCache();
        if (!($config = $cache->load($cacheId)) || $refresh) {
            $config = new Zend_Config_Ini(APPLICATION_PATH . '/configs/application.ini', APPLICATION_ENV);
            $cache->save($config, $cacheId);
            if ($returnArray === true) {
                $cache->save($config->toArray(), $cacheId);
            }
        }
        
        return $config;
    }
    
    public static function loadForm($moduleName, $formName)
    {
    	$cacheId = $moduleName."_".$formName."_form";
    	$cache = App::getCache();
    	if (!($form = $cache->load($cacheId))) {
    		$fileName = APPLICATION_PATH."/modules/".
    					$moduleName."/forms/".$formName.".xml";
    		$form = Core_Form_Xml::loadForm(file_get_contents($fileName));
    		$cache->save($form, $cacheId);
    	}
    	return $form;
    }
    
    public static function getRoutes($refresh = true)
    {
        $cacheId = 'appRoutes';
        $cache = App::getCache();
        if (!($routes = $cache->load($cacheId)) || $refresh) {
            $routes = new Zend_Config_Ini(APPLICATION_PATH . '/configs/routes.ini', APPLICATION_ENV);
            $cache->save($routes, $cacheId);
        }

        return $routes;
    }
    
    /**
     * Returns module level configuration, 
     * cache configuration if cache is enabled,
     * returns in array format if returnArray is set to true
     * 
     * @param string  $moduleName
     * @param boolean $returnArray
     * @param boolean $cache
     */
    public static function getModuleConfig($moduleName, $returnArray = false, $cache = false)
    {
        $config = new Zend_Config_Ini($moduleName."/etc/config.ini");
        if ($returnArray === true) {
            return $config->toArray();
        }
        return $config;
        //TODO cache config if cache is true
    }
    
    /**
     * Returns model objects in a particular module 
     * use /*@var $varName ObjectType *//* to make the dynamic
     * object detected by eclipse
     * 
     * @param string $moduleName - Name of the module
     * @param string $modelName  - Name of the model
     * @return unknown
     */
    public static function getModel($moduleName, $modelName, $cache = false)
    {
        try {
            require_once $moduleName."/models/".ucfirst($modelName).".php";
            $model = new $modelName();
            return $model;
        } catch (Exception $e){
            throw new Exception($e->getMessage());
        }
        //TODO Implement Caching
    }
    
    public static  function getDAO($moduleName, $daoName, $cache = false)
    {
        $daoName = ucfirst($daoName) . "DAO";
        try {
            require_once $moduleName . "/models/DAO/" . $daoName . ".php";
            $dao = new $daoName();
            return $dao;
        } catch (Exception $e){
            throw new Exception($e->getMessage());
        }
        //TODO Implement Caching
    }
    
    /**
     * Returns Form object
     * 
     * @param string  $moduleName - Module Name where form exists
     * @param string  $formName   - Form name 
     * @param array   $options    - Array of options
     * @param boolean $cache      - Whether to return from cache or not
     */
    public static function getForm($moduleName, $formName, $options = null, $cache = true)
    {
        $formClassName = ucfirst($formName)."Form";
        require_once "Core/Form/Abstract.php";
        require_once "Core/Form.php";
        require_once $moduleName."/forms/".$formClassName.".php";
        if (!$cache) {
            return new $formClassName();
            //return call_user_func($formClassName."::getForm");
            //return $formClassName."::getForm";
        }
        $cache = App::getCache();
        if (!($form = $cache->load($formClassName))) {
            try {
                /* @var $formClassName Core_Form_Abstract */
                return new $formClassName();
                $cache->save($form, $formClassName);
            } catch (Exception $e){
                
                throw new Exception("Form doesn`t exist ".$e->getMessage());
            }
        }
        return $form;
        //TODO Implement Caching
    }
    
    public static function getHelloWorld()
    {
        return "Hello World";
    }
    
    /**
     * Check whether the cache is enabled or not
     * 
     * @return boolean
     */
    public static function isCacheEnabled()
    {
        
    }
    
    /**
     * Returns Cache object based on type
     * 
     * @param string $type - Type of cache (Apc or File or Memcached) 
     * @param int    $time - Time for which cach object will cache   
     */
    public static function getCache($type = 'Apc', $time = 3600) 
    {
        // include the cache class
        require_once('Zend/Cache.php');
        require_once('Zend/Cache/Core.php');
        require_once('Zend/Cache/Backend/Apc.php');
        
        // this way we can have specific cache objects for ex: "Apc", "File", "Memcache", etc
        $cacheKey = "cacheObj" . $type;
        
        // add the zend-cache object to the core-cache manager "APC" if this is not available
        // then use "File" manager.
        $isDefaultCacheAvailable = App::isCacheAvailable("Apc");
        if ($isDefaultCacheAvailable == "Apc") {
            $cacheObj = unserialize(apc_fetch($cacheKey));
            
            if ($cacheObj instanceof Zend_Cache_Core) {
                return $cacheObj;
            }
        } elseif (file_exists(self :: getPath('cache') . $cacheKey)) {
            $cacheObj = unserialize(file_get_contents(self :: getPath('cache') . $cacheKey));
            
            return $cacheObj; 
        } 
        
        // front end options, cache for 1 minute
        $frontendOptions = array(
           'lifetime'                => $time,
           'automatic_serialization' => true
        );
        
        //back end options used to store the data
        $backendOptions = array();
        
        if (App::isCacheAvailable($type) != $type) {
            $type = "File";
            //throw new Exception("The given cache manager is not available."); 
        }
        
        if ($type == 'File') {
        	$backendOptions = array('cache_dir' => self :: getPath('cache'));
        }
        
        $cacheObj = Zend_Cache::factory(
            "Core", 
            $type,
            $frontendOptions,
            $backendOptions
        );
        
        // add the object to this cache
        if ($isDefaultCacheAvailable != "Apc") {
            file_put_contents(self :: getPath('cache') . "$cacheKey", serialize($cacheObj));
        } else {
            apc_add($cacheKey, serialize($cacheObj));
        }

        return $cacheObj;
    }
    
    /**
     *  This function will help us identify whether the given cache-manager is loaded in php.ini 
     *  or not.
     *  
     *  @param string $type - cache-manager can be "APC"/"File"/"Memcache" etc
     *  
     *  @return string $type - default type or given type.  
     *
     */
    public static function isCacheAvailable($type)
    {
        if ( !extension_loaded($type) ) {
            //throw new Exception("APC Cache is not installed or enabled.");
            return "File";
        }
        
        return $type;
    }
    
    /**
     * It will clean cache
     */
    public static function cleanCache()
    {
        App::getCache()->clean();
    }
    
    
    
    /**
     * return paths depending on type
     * 
     * @param string $type - valid type: cache, captcha, logs, temp
     */
    public static function getPath($type = null)
    {
        $typeArray = array("cache", "captcha", "logs", "temp");
        if ($type === null || !in_array($type, $typeArray)) {
            throw new Exception("Type not found");
        } if ($type == 'captcha') {
            return APPLICATION_PATH . '/../public/captcha';
        } else {
            return self::getStoragePath() . '/' . $type . '/';
        }
        
    }
    
    /**
     * Get storage folder path
     */
    public static function getStoragePath()
    {
        return APPLICATION_PATH . '/../storage';
    }
    
    /**
     * Return app version number
     */
    public static function getVersion()
    {
        return self::$_version;
    }
    
    /**
     * Check Dependencies
     */
    public function checkDependencies()
    {
        $requiredExtensions = self::getRequiredExtensions();
        foreach ( $requiredExtensions as $value ) {
            if ( !extension_loaded($value) ) {
                throw new Exception("Required PHP extension '{$value}' was not loaded.");
            }
        }
    }
    
    /**
     * Return all the required php extensions required
     * for this application
     */
    public static function getRequiredExtensions()
    {
        //TODO Get it from external file
        return array('gd', 'mysqli');
    }
}